package com.iamk.util;

import java.awt.Color;
import java.awt.image.BufferedImage;

public class HaarTransform {
	private final double w0 = 0.5;
	private final double w1 = -0.5;
	private final double s0 = 0.5;
	private final double s1 = 0.5;
	
	/**
	 * Method calculate HaarWavelet toward Image
	 * @param BufferedImage
	 * 		--> Original Image
	 * @param int
	 * 		--> Number iteraction
	 * @return BufferedImage
	 * 		--> Transform Image
	 * */
	public BufferedImage foward(BufferedImage img,int Iterations){
		int width = img.getWidth();
		int height = img.getHeight();
		BufferedImage imgWavelet = new BufferedImage(width,height,BufferedImage.TYPE_INT_RGB);
		int maxScale = (int)(Math.log(width < height ? width : height) / Math.log(2));
		if (Iterations < 1 || Iterations > maxScale)
        {
            System.out.println("Iteration must be Integer from 1 to " + maxScale);
        }
		double[][] channelRed = new double[width][height];
	    double[][] channelGreen = new double[width][height];
	    double[][] channelBlue = new double[width][height];
	   
	    for(int i=0; i<height; i++)
	    {
	        for(int j=0; j<width; j++)
	        {
	        	int rgb = img.getRGB(j,i);
	        	int red = rgb >>16 & 0xff;
	        	int green = rgb>>8 & 0xff;
				int blue = rgb & 0xff;
	        	channelRed[j][i] = (double)Scale(0, 255, -1, 1, red);
	        	channelGreen[j][i] = (double)Scale(0, 255, -1, 1, green);
	        	channelBlue[j][i] = (double)Scale(0, 255, -1, 1, blue);
	        }
	    }
	    
	    FWT(channelRed, Iterations);
        FWT(channelGreen, Iterations);
        FWT(channelBlue, Iterations);
        
        for (int i = 0; i < height; i++)
        {
            for (int j = 0; j < width; j++)
            {
            	imgWavelet.setRGB(j, i, 
                		(new Color((int)Scale(-1, 1, 0, 255, channelRed[j][i]), 
                				   (int)Scale(-1, 1, 0, 255, channelGreen[j][i]),
                				   (int)Scale(-1, 1, 0, 255, channelBlue[j][i]))).getRGB());
            }
        }
		return imgWavelet;
	}
	
	/**
	 * Method calculate HaarWavelet reverse Image
	 * @param BufferedImage
	 * 		--> Original Image
	 * @param int
	 * 		--> Number iteraction
	 * @return BufferedImage
	 * 		--> Transform Image
	 * */
	
	public BufferedImage reverse(BufferedImage img,int Iterations){
		int width = img.getWidth();
		int height = img.getHeight();
		BufferedImage imgOriginal = new BufferedImage(width,height,BufferedImage.TYPE_INT_RGB);
		int maxScale = (int)(Math.log(width < height ? width : height) / Math.log(2));
		if (Iterations < 1 || Iterations > maxScale)
        {
            System.out.println("Iteration must be Integer from 1 to " + maxScale);
        }
		double[][] channelRed = new double[width][height];
	    double[][] channelGreen = new double[width][height];
	    double[][] channelBlue = new double[width][height];
	   
	    for(int i=0; i<height; i++)
	    {
	        for(int j=0; j<width; j++)
	        {
	        	int rgb = img.getRGB(j,i);
	        	int red = rgb >>16 & 0xff;
	        	int green = rgb>>8 & 0xff;
				int blue = rgb & 0xff;
	        	channelRed[j][i] = (double)Scale(0, 255, -1, 1, red);
	        	channelGreen[j][i] = (double)Scale(0, 255, -1, 1, green);
	        	channelBlue[j][i] = (double)Scale(0, 255, -1, 1, blue);
	        }
	    }
	    
	    IWT(channelRed, Iterations);
        IWT(channelGreen, Iterations);
        IWT(channelBlue, Iterations);
        
        for (int i = 0; i < height; i++)
        {
            for (int j = 0; j < width; j++)
            {
            	imgOriginal.setRGB(j, i, 
                		(new Color((int)Scale(-1, 1, 0, 255, channelRed[j][i]), 
                				   (int)Scale(-1, 1, 0, 255, channelGreen[j][i]),
                				   (int)Scale(-1, 1, 0, 255, channelBlue[j][i]))).getRGB());
            }
        }
		return imgOriginal;
	}
	
	 public double Scale(double fromMin, double fromMax, double toMin, double toMax, double x)
     {
         if (fromMax - fromMin == 0) return 0;
         double value = (toMax - toMin) * (x - fromMin) / (fromMax - fromMin) + toMin;
         if (value > toMax)
         {
             value = toMax;
         }
         if (value < toMin)
         {
             value = toMin;
         }
         return value;
     }
	 
	 /// <summary>
     ///   Discrete Haar Wavelet Transform
     /// </summary>
     /// 
     public void FWT(double[] data)
     {
         double[] temp = new double[data.length];

         int h = data.length >> 1;
         for (int i = 0; i < h; i++)
         {
             int k = (i << 1);
             temp[i] = data[k] * s0 + data[k + 1] * s1;
             temp[i + h] = data[k] * w0 + data[k + 1] * w1;
         }

         for (int i = 0; i < data.length; i++)
             data[i] = temp[i];
     }

     /// <summary>
     ///   Discrete Haar Wavelet 2D Transform
     /// </summary>
     /// 
     public void FWT(double[][] data, int iterations)
     {
         int rows = data.length;
         int cols = data[0].length;

         double[] row;
         double[] col;

         for (int k = 0; k < iterations; k++)
         {
             int lev = 1 << k;
             
             int levCols = cols / lev;
             int levRows = rows / lev;

             row = new double[levCols];
             for (int i = 0; i < levRows; i++)
             {
                 for (int j = 0; j < row.length; j++)
                     row[j] = data[i][j];

                 FWT(row);

                 for (int j = 0; j < row.length; j++)
                     data[i][j] = row[j];
             }

             
             col = new double[levRows];
             for (int j = 0; j < levCols; j++)
             {
                 for (int i = 0; i < col.length; i++)
                     col[i] = data[i][j];

                 FWT(col);

                 for (int i = 0; i < col.length; i++)
                     data[i][j] = col[i];
             }
         }
     }

     /// <summary>
     ///   Inverse Haar Wavelet Transform
     /// </summary>
     /// 
     public void IWT(double[] data)
     {
         double[] temp = new double[data.length];

         int h = data.length >> 1;
         for (int i = 0; i < h; i++)
         {
             int k = (i << 1);
             temp[k] = (data[i] * s0 + data[i + h] * w0) / w0;
             temp[k + 1] = (data[i] * s1 + data[i + h] * w1) / s0;
         }

         for (int i = 0; i < data.length; i++)
             data[i] = temp[i];
     }

     /// <summary>
     ///   Inverse Haar Wavelet 2D Transform
     /// </summary>
     /// 
     public void IWT(double[][] data, int iterations)
     {
         int rows = data.length;
         int cols = data[0].length;

         double[] col;
         double[] row;

         for (int k = iterations - 1; k >= 0; k--)
         {
             int lev = 1 << k;

             int levCols = cols / lev;
             int levRows = rows / lev;

             col = new double[levRows];
             for (int j = 0; j < levCols; j++)
             {
                 for (int i = 0; i < col.length; i++)
                     col[i] = data[i][j];

                 IWT(col);

                 for (int i = 0; i < col.length; i++)
                     data[i][j] = col[i];
             }

             row = new double[levCols];
             for (int i = 0; i < levRows; i++)
             {
                 for (int j = 0; j < row.length; j++)
                     row[j] = data[i][j];

                 IWT(row);

                 for (int j = 0; j < row.length; j++)
                     data[i][j] = row[j];
             }
         }
     }
}
